/// <reference path="Vector2.ts" />
module ew {
    export class Matrix {
        public m11: number;
        public m12: number;
        public m13: number;
        public m14: number;
        public m21: number;
        public m22: number;
        public m23: number;
        public m24: number;
        public m31: number;
        public m32: number;
        public m33: number;
        public m34: number;
        public m41: number;
        public m42: number;
        public m43: number;
        public m44: number;      

        public static identity = new Matrix(
            1, 0, 0, 0, 
            0, 1, 0, 0, 
            0, 0, 1, 0, 
            0, 0, 0, 1);
        public static zero = new Matrix(
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0, 
            0, 0, 0, 0);

        constructor(m11: number,
            m12: number,
            m13: number,
            m14: number,
            m21: number,
            m22: number,
            m23: number,
            m24: number,
            m31: number,
            m32: number,
            m33: number,
            m34: number,
            m41: number,
            m42: number,
            m43: number,
            m44: number) {
            this.m11 = m11;
            this.m12 = m12;
            this.m13 = m13;
            this.m14 = m14;
            this.m21 = m21;
            this.m22 = m22;
            this.m23 = m23;
            this.m24 = m24;
            this.m31 = m31;
            this.m32 = m32;
            this.m33 = m33;
            this.m34 = m34;
            this.m41 = m41;
            this.m42 = m42;
            this.m43 = m43;
            this.m44 = m44;
        }

        private static createEmpty() {
            return new Matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
        }

        public multiply(matrix: Matrix): Matrix;
        public multiply(scalar: number): Matrix;

        public multiply(value: any): Matrix {
            if (typeof value == "number") {
                return new Matrix(
                    this.m11 * value, this.m12 * value, this.m13 * value, this.m14 * value,
                    this.m21 * value, this.m22 * value, this.m23 * value, this.m24 * value,
                    this.m31 * value, this.m32 * value, this.m33 * value, this.m34 * value,
                    this.m41 * value, this.m42 * value, this.m43 * value, this.m44 * value
                    );
            }
            return new Matrix(
                this.m11 * value.m11 + this.m12 * value.m21 + this.m13 * value.m31 + this.m14 * value.m41,
                this.m11 * value.m12 + this.m12 * value.m22 + this.m13 * value.m32 + this.m14 * value.m42,
                this.m11 * value.m13 + this.m12 * value.m23 + this.m13 * value.m33 + this.m14 * value.m43,
                this.m11 * value.m14 + this.m12 * value.m24 + this.m13 * value.m34 + this.m14 * value.m44,
                this.m21 * value.m11 + this.m22 * value.m21 + this.m13 * value.m21 + this.m24 * value.m41,
                this.m21 * value.m12 + this.m22 * value.m22 + this.m13 * value.m22 + this.m24 * value.m42,
                this.m21 * value.m13 + this.m22 * value.m23 + this.m13 * value.m23 + this.m24 * value.m43,
                this.m21 * value.m14 + this.m22 * value.m24 + this.m13 * value.m24 + this.m24 * value.m44,
                this.m31 * value.m11 + this.m32 * value.m21 + this.m33 * value.m31 + this.m34 * value.m41,
                this.m31 * value.m12 + this.m32 * value.m22 + this.m33 * value.m32 + this.m34 * value.m42,
                this.m31 * value.m13 + this.m32 * value.m23 + this.m33 * value.m33 + this.m34 * value.m43,
                this.m31 * value.m14 + this.m32 * value.m24 + this.m33 * value.m34 + this.m34 * value.m44,
                this.m41 * value.m11 + this.m42 * value.m21 + this.m13 * value.m31 + this.m44 * value.m41,
                this.m41 * value.m12 + this.m42 * value.m22 + this.m13 * value.m32 + this.m44 * value.m42,
                this.m41 * value.m13 + this.m42 * value.m23 + this.m13 * value.m33 + this.m44 * value.m43,
                this.m41 * value.m14 + this.m42 * value.m24 + this.m13 * value.m34 + this.m44 * value.m44
                );
        }

        public negate(): Matrix {
            var negated = new Matrix(            
                -this.m11,
                -this.m12,
                -this.m13,
                -this.m14,
                -this.m21,
                -this.m22,
                -this.m23,
                -this.m24,
                -this.m31,
                -this.m32,
                -this.m33,
                -this.m34,
                -this.m41,
                -this.m42,
                -this.m43,
                -this.m44)

            return negated;
        }

        public add(matrix: Matrix): Matrix {
            var sum = new Matrix(
                this.m11 + matrix.m11,
                this.m12 + matrix.m12,
                this.m13 + matrix.m13,
                this.m14 + matrix.m14,
                this.m21 + matrix.m21,
                this.m22 + matrix.m22,
                this.m23 + matrix.m23,
                this.m24 + matrix.m24,
                this.m31 + matrix.m31,
                this.m32 + matrix.m32,
                this.m33 + matrix.m33,
                this.m34 + matrix.m34,
                this.m41 + matrix.m41,
                this.m42 + matrix.m42,
                this.m43 + matrix.m43,
                this.m44 + matrix.m44);

            return sum;
        }

        public substract(matrix: Matrix): Matrix {
            var diff = new Matrix(
                this.m11 - matrix.m11,
                this.m12 - matrix.m12,
                this.m13 - matrix.m13,
                this.m14 - matrix.m14,
                this.m21 - matrix.m21,
                this.m22 - matrix.m22,
                this.m23 - matrix.m23,
                this.m24 - matrix.m24,
                this.m31 - matrix.m31,
                this.m32 - matrix.m32,
                this.m33 - matrix.m33,
                this.m34 - matrix.m34,
                this.m41 - matrix.m41,
                this.m42 - matrix.m42,
                this.m43 - matrix.m43,
                this.m44 - matrix.m44);

            return diff;
        }

        // Not tested
        public transform(vector: Vector2): Vector2 {
            var transformed = new Vector2(0, 0);
            transformed.x = this.m11 * vector.x + this.m12 * vector.y + this.m13;
            transformed.y = this.m21 * vector.x + this.m22 * vector.y + this.m23;

            return transformed;
        }

        public static lerp(matrix1: Matrix, matrix2: Matrix, amount: number): Matrix {
            var interpolated = new Matrix(
                matrix1.m11 + (matrix2.m11 - matrix1.m11) * amount,
                matrix1.m12 + (matrix2.m12 - matrix1.m12) * amount,
                matrix1.m13 + (matrix2.m13 - matrix1.m13) * amount,
                matrix1.m14 + (matrix2.m14 - matrix1.m14) * amount,
                matrix1.m21 + (matrix2.m21 - matrix1.m21) * amount,
                matrix1.m22 + (matrix2.m22 - matrix1.m22) * amount,
                matrix1.m23 + (matrix2.m23 - matrix1.m23) * amount,
                matrix1.m24 + (matrix2.m24 - matrix1.m24) * amount,
                matrix1.m31 + (matrix2.m31 - matrix1.m31) * amount,
                matrix1.m32 + (matrix2.m32 - matrix1.m32) * amount,
                matrix1.m33 + (matrix2.m33 - matrix1.m33) * amount,
                matrix1.m34 + (matrix2.m34 - matrix1.m34) * amount,
                matrix1.m41 + (matrix2.m41 - matrix1.m41) * amount,
                matrix1.m42 + (matrix2.m42 - matrix1.m42) * amount,
                matrix1.m43 + (matrix2.m43 - matrix1.m43) * amount,
                matrix1.m44 + (matrix2.m44 - matrix1.m44) * amount);

            return interpolated;
        }
    }
}